package core

import (
	"fmt"
	"ginvueadmin/global"
	"ginvueadmin/utils"
	"os"
	"time"

	zaprotatelogs "github.com/lestrrat-go/file-rotatelogs"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	err    error
	level  zapcore.Level
	writer zapcore.WriteSyncer
)

// 对于日志记录方面进行的一些初始化操作，包括创建日志写入器、设置日志级别、是否记录调用信息等
func init() {
	/*
		1. 首先判断是否存在指定路径的 Director 文件夹
			a. 如果不存在则创建。
		2. 根据配置文件中的 Level 字段，设置日志记录的级别。
		3. 调用 getWriteSyncer 函数获取日志的写入器，getWriteSyncer 函数使用了 file-rotatelogs 进行日志分割。
			a. 判断 err 情况
		4. 根据设置的日志级别和是否需要堆栈跟踪，使用 zap.New 函数创建一个新的 Logger 对象。
		5. 根据配置文件中的 ShowLine 字段，决定是否记录日志的调用信息。
	*/
	if ok, _ := utils.DirExists(global.CONFIG.Zap.Director); !ok {
		fmt.Printf("create %v directory", global.CONFIG.Zap.Director)
		os.Mkdir(global.CONFIG.Zap.Director, os.ModePerm)
	}

	// 初始化配置文件 level
	// debug info warn error dpanic panic fatal
	switch global.CONFIG.Zap.Level {
	case "debug":
		level = zap.DebugLevel
	case "info":
		level = zap.InfoLevel
	case "warn":
		level = zap.WarnLevel
	case "error":
		level = zap.ErrorLevel
	case "dpanic":
		level = zap.DPanicLevel
	case "panic":
		level = zap.PanicLevel
	case "fatal":
		level = zap.FatalLevel
	}

	// 使用 file-rotatelogs 进行日志分割
	writer, err = getWriteSyncer()
	if err != nil {
		fmt.Printf("Get write syncer failed err: %v", err)
		return
	}

	if level == zap.DebugLevel || level == zap.ErrorLevel {
		global.LOG = zap.New(getEncoderCore(), zap.AddStacktrace(level))
	} else {
		global.LOG = zap.New(getEncoderCore())
	}

	if global.CONFIG.Zap.ShowLine {
		global.LOG.WithOptions(zap.AddCaller())
	}
}

// getEncoderCore 获取 Encoder 的 zapcore.Core
func getEncoderCore() (core zapcore.Core) {
	return zapcore.NewCore(getEncoder(), writer, level)
}

func getEncoder() (enc zapcore.Encoder) {
	if global.CONFIG.Zap.Format == "json" {
		return zapcore.NewJSONEncoder(getEncoderConfig())
	}
	return zapcore.NewConsoleEncoder(getEncoderConfig())
}

// getEncoderConfig 获取 zapcore.EncoderConfig
func getEncoderConfig() (cfg zapcore.EncoderConfig) {
	config := zapcore.EncoderConfig{
		MessageKey:     "message",
		LevelKey:       "level",
		TimeKey:        "time",
		NameKey:        "logger",
		CallerKey:      "caller",
		StacktraceKey:  global.CONFIG.Zap.StacktraceKey,
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     CustomTimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.FullCallerEncoder,
	}

	// LowercaseLevelEncoder: 小写, LowercaseColorLevelEncoder: 小写带颜色, CapitalLevelEncoder: 大写, CapitalColorLevelEncoder: 大写带颜色
	switch {
	case global.CONFIG.Zap.EncodeLevel == "LowercaseLevelEncoder":
		config.EncodeLevel = zapcore.LowercaseLevelEncoder
	case global.CONFIG.Zap.EncodeLevel == "LowercaseColorLevelEncoder":
		config.EncodeLevel = zapcore.LowercaseColorLevelEncoder
	case global.CONFIG.Zap.EncodeLevel == "CapitalLevelEncoder":
		config.EncodeLevel = zapcore.CapitalLevelEncoder
	case global.CONFIG.Zap.EncodeLevel == "CapitalColorLevelEncoder":
		config.EncodeLevel = zapcore.CapitalColorLevelEncoder
	}

	return config
}

// getWriteSyncer zap logger 中加入 file-rotatelogs
func getWriteSyncer() (zapcore.WriteSyncer, error) {
	/*
		1. 首先通过调用 zaprotatelogs.New() 函数创建了一个支持日志切割的文件写入对象 fileWriter。
			a. global.GVA_CONFIG.Zap.Director+string(os.PathSeparator)+"%Y-%m-%d.log" 指定了日志文件的路径和文件名
			b. zaprotatelogs.WithLinkName(global.GVA_CONFIG.Zap.LinkName) 指定了软链接的名称
			c. zaprotatelogs.WithMaxAge(7*24*time.Hour) 指定了最大保存时间为 7 天
			d. zaprotatelogs.WithRotationTime(24*time.Hour) 指定了日志切割的时间间隔为 1 天
		2. 然后，通过判断全局配置中的 LogInConsole 标志
			a. 如果为真，则使用 zapcore.NewMultiWriteSyncer() 函数创建一个多重写入器，将日志同时输出到控制台和文件中。
				i. 其中 zapcore.AddSync(os.Stdout) 表示将日志输出到标准输出，zapcore.AddSync(fileWriter) 表示将日志输出到文件。
			b. 如果 LogInConsole 为假，则直接使用 zapcore.AddSync(fileWriter) 将日志输出到文件。
		3. 最后，函数返回一个 zapcore.WriteSyncer 类型和可能出现的错误。
	*/

	fileWriter, err := zaprotatelogs.New(
		global.CONFIG.Zap.Director+string(os.PathSeparator)+"%Y-%m-%d.log",
		zaprotatelogs.WithLinkName(global.CONFIG.Zap.LinkName),
		zaprotatelogs.WithMaxAge(7*24*time.Hour),
		zaprotatelogs.WithRotationTime(24*time.Hour),
	)

	if global.CONFIG.Zap.LogInConsole {
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err
	}

	return zapcore.AddSync(fileWriter), err
}

// 自定义日志输出时间格式
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format(global.CONFIG.Zap.Prefix + "2006/01/02 - 15:04:05.000"))
}
